using Godot;
using System;
using Godot.Collections;

using CurrentGame.NPC;
using CurrentGame.Utils.Interpolation;

namespace CurrentGame.GUI.PlaceGrid;


public partial class PlaceModeGrid : TileMapLayer
{
	// GODOT - Signals
	[Signal]
	public delegate void SelectGridChangedEventHandler(Vector2I gridPosition, Vector2 globalPosition, bool available);
	[Signal]
	public delegate void EnableStateChangedEventHandler(bool enabled);

	[Export]
	public bool FreePlaceCost { get; set; } = false;

	[ExportGroup("Grid settings")]
	[Export]
	public int GridSize { get; set; } = 32;
	[Export]
	public Vector2I LeftTopCoordinate { get; set; }
	[Export]
	public Vector2I RightBottomCoordinate { get; set; }

	[ExportGroup("Animation")]
	[Export]
	public float ActivatedAlpha { get; set; } = 0.5f;
	[Export]
	public float InterpolationWeight
	{
		get => _interpolation.Weight;
		set => _interpolation.Weight = value;
	}

	[ExportGroup("Hint display")]
	[Export]
	public float HintFadeFactor { get; set; } = 0.1f;
	[Export]
	public float HintStayDuration { get; set; } = 5.0f;

	[ExportGroup("Node reference")]
	[Export]
	public Player MainPlayer { get; set; }
	[Export]
	public CinematicCamera GameCamera { get; set; }
	[Export]
	public SelectBox AnchorSprite { get; set; }
	[Export]
	public GamePlayGui GameGUI { get; set; }


	// private members
	private LerpInterpolation _interpolation;
	private bool _tileEnabled = false;
    private System.Collections.Generic.Queue<ShadeCharacter> _readyShadeQueue, _notReadyShadeQueue;
	private bool _positionAvailale = false;

    // Child references
    private GridSelector _gridSelector;


	public PlaceModeGrid()
	{
		_interpolation = new LerpInterpolation(0.01f, new Vector3(0.001f, -1f, -1f))
		{
			Value = 0.0f
		};
		_interpolation.Teleport();
		_interpolation.ReachStableValue += Signal_InterpolationStable;
		_readyShadeQueue = [];
		_notReadyShadeQueue = [];
	}

	// GODOT overrides
	public override void _Ready()
	{
		_gridSelector = GetNode<GridSelector>("GridSelector");
		GameGUI.SetPlacementHintAnchor(AnchorSprite.Anchor);

		Modulate = new Color(1.0f, 1.0f, 1.0f, 0.0f);
	}

	public override void _PhysicsProcess(double delta)
	{
		// Position update
		var playerPosition = ToLocal(MainPlayer.GlobalPosition);
		var gridPostion = LocalToMap(playerPosition);
		var currenPosition = (Vector2I)GlobalPosition;
		 
		currenPosition += gridPostion * GridSize;
		GlobalPosition = currenPosition;
		ResetPhysicsInterpolation();

		// Action update
		if (_tileEnabled && Input.IsActionJustPressed("confirm_place"))
		{
			// Check for placing shade
			if (_gridSelector.CheckAvailable()) PlaceShade();
		}
	}

    public override void _Process(double delta)
    {
        if (_interpolation.Process(delta))
		{
			Modulate = new Color(1.0f, 1.0f, 1.0f, _interpolation.Value);
		}
    }

	// Private functions
	private void UpdatePlaceHint()
	{
		string hintString;
		if (_positionAvailale)
		{
			if (_readyShadeQueue.Count > 0) hintString = $"剩余 {_readyShadeQueue.Count}";
			else hintString = "消耗 1 光";
		}
		else
		{
			hintString = "位置不可用";
		}
		GameGUI.ChangePlacementHint(hintString);
	}

	private void PlaceShade()
	{
		// Check ready shades first
		ShadeCharacter shade;
		if (_readyShadeQueue.Count > 0) shade = _readyShadeQueue.Dequeue();
		else
		{
			shade = _notReadyShadeQueue.Dequeue();
			// HP - 1
			MainPlayer.HealthPoint -= 1;
		}
		// Push in Queue
		if (FreePlaceCost) _readyShadeQueue.Enqueue(shade);
		else _notReadyShadeQueue.Enqueue(shade);
		// Teleport shade to located position
		shade.TeleportTo(_gridSelector.GlobalPosition);
		// Update hint
		UpdatePlaceHint();
    }

	// Export interfaces
	public void EnablePlaceMode()
	{
		_tileEnabled = true;
		_interpolation.Value = ActivatedAlpha;
		_gridSelector.Activated = true;
		GameGUI.ShowPlacementHint();
		UpdatePlaceHint();
		EmitSignal(SignalName.EnableStateChanged, true);
	}

	public void DisablePlaceMode()
	{
		_tileEnabled = false;
		_interpolation.Value = 0.0f;
		_gridSelector.Activated = false;
		GameGUI.HidePlacementHint();
		EmitSignal(SignalName.EnableStateChanged, false);
    }

	public void NotifySelectionChange(Vector2I grid, Vector2 global)
	{
		var available = _gridSelector.CheckAvailable();
		if (available != _positionAvailale)
		{
			_positionAvailale = available;
			UpdatePlaceHint();
		}
		EmitSignal(SignalName.SelectGridChanged, grid, global, available);
	}

	public bool CheckSelectionAvailable()
	{
		return _gridSelector.CheckAvailable();
	}

	public void AppendReadyShade(ShadeCharacter shade)
	{
		_readyShadeQueue.Enqueue(shade);
	}

	public void AppendNotReadyShade(ShadeCharacter shade)
	{
		_notReadyShadeQueue.Enqueue(shade);
	}

	public void ClearAllShade()
	{
		_readyShadeQueue.Clear();
		_notReadyShadeQueue.Clear();
	}

	// NOT GODOT - EventHandlers
	private void Signal_InterpolationStable(float v, Vector2 v2, Vector3 v3)
	{

	}
}
